package Projekt;

import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionAdapter;
import java.util.*;



public class PaintPanel extends JPanel{
	
	private static final long serialVersionUID = 1L;
	//Eigenschaften von PaintPanel
	private Color fillColor,lineColor;			//Fuellfarbe, Zeichenfarbe
	private boolean drawMode = false;		//Zeichenmodus
	private boolean fillMode = false;   	//Fuellmodi
	private int cornerCount = 0;

	
	Vector<Paintable> polygonList = new Vector<Paintable>();
	Vector<Paintable> lineList = new Vector<Paintable>();
	Vector<Paintable> tmplist = new Vector<Paintable>();
	//Vector zum Abspeichern der bereits fertig gezeichneten Objekte
	
	Polygon actPolygon = new Polygon();
	Line actLine = new Line();
	Corner corner = new Corner();
	private boolean shadeMode = false;
	private int shadeDistance;

   //Konstruktor
	public PaintPanel(){
		this.setBackground(Color.white);
		this.setFillColor(Color.GREEN);	//Initialisierung der 
		this.setLineColor(Color.BLACK);    //Eigenschaften
		this.setFillMode(false);
		this.setDrawMode(false);
	   //Zuordnung der Mouse- und MouseMotionListener
		this.setFocusable(true);
		this.addKeyListener(new KeyListener() {
			@Override
			public void keyTyped(KeyEvent e) {
				if(e.getKeyChar() == KeyEvent.VK_ENTER){
					if(cornerCount > 2)
						setPolyEnde();
					else
						System.out.println("Es muessen mehr als 2 Punkte angegeben werden!");
				}			
				if(e.getKeyChar() == KeyEvent.VK_ESCAPE){
					undoLine();
				}
			}
			@Override
			public void keyPressed(KeyEvent e) {
			}
			@Override
			public void keyReleased(KeyEvent e) {
			}
		});
		
		this.addMouseListener(new MouseAdapter(){
			public void mousePressed(MouseEvent e) {
				double distance;
				double minDistance[];
				int i, selected = 0, t, polygonCount = 0;
				Point p = new Point();
				p.x = e.getX();
				p.y = e.getY();
				if(!fillMode && !shadeMode){
					if(drawMode){
						if(cornerCount==0){
							actPolygon = new Polygon();
							corner = new Corner();
							actLine = new Line();
						}else{	
							actLine = new Line();
							actLine.setP1(actPolygon.getP(cornerCount-1));
							corner = new Corner();
						}
						actPolygon.setCol(lineColor);
						actLine.setCol(lineColor);
					}
				}else{
					actPolygon = new Polygon();
					polygonCount = polygonList.size();
					minDistance = new double[polygonCount];
					for(t = 0;t < polygonCount;t++){
						i = 0;
						actPolygon = (Polygon)polygonList.get(t);
						System.out.println("punkteanzahl"+t+": "+actPolygon.getPointNumber());
						while(actPolygon.getP(i) != null){
							if(actPolygon.getP(i+1) == null)
								distance = Line.ptLineDist(actPolygon.getP(i).x, actPolygon.getP(i).y, actPolygon.getP(0).x, actPolygon.getP(0).y, p.x, p.y);
							else
								distance = Line.ptLineDist(actPolygon.getP(i).x, actPolygon.getP(i).y, actPolygon.getP(i+1).x, actPolygon.getP(i+1).y, p.x, p.y);
							if(i == 0)
								minDistance[t] = distance;
							else if(distance < minDistance[t])
								minDistance[t] = distance;
							i++;
						}
						if(t > 0){
							if(minDistance[t] < minDistance[selected]){
								selected = t;
							}
						}	
					}
					for(t=0;t<polygonCount;t++){
						System.out.println("Polygon "+t+": "+minDistance[t]);
					}
					System.out.println("Mindestabstand Polygon "+selected+": "+minDistance[selected]);
					if(minDistance[selected] < 1)
						fillPolygon(selected, shadeDistance);
				}
			}
			public void mouseReleased(MouseEvent e) {
				Point p = new Point();
				p.x = e.getX();
				p.y = e.getY();
				if(drawMode){
					corner.point = p;
					tmplist.add(corner);
					actPolygon.setP(p, cornerCount);
					if(cornerCount == 0)
						actLine.setP1(p);
					else{
						actLine.setP2(p);
						tmplist.add(actLine);
					}
					actLine = null;
					cornerCount++;
				}
				repaint();	
			}	
		});
		this.addMouseMotionListener(new MouseMotionAdapter(){
			public void mouseDragged(MouseEvent e) {
				Point p = new Point();
				p.x = e.getX();
				p.y = e.getY();
				if(drawMode){
					actLine.setCol(lineColor);
					actLine.setP2(p);
					corner.point = p;
				}
				repaint();
			}
		});
	}
	
	/**
	 * Zum Fuellen eines Polygons mittels des Scanline-Verfahrens
	 * @param selected ist das ausgewählte Polygon
	 * @param dist TODO
	 */
	public void fillPolygon(int selected, int dist){
		actPolygon = new Polygon();
		actPolygon = (Polygon)polygonList.get(selected);
		int minY = 0, maxY = 0, pointNumber = 0;
		double  ix = 0;
		Point points[] = new Point[actPolygon.getPointNumber()];		
		for(int i = 0;i < actPolygon.getPointNumber();i++){
			points[i]=actPolygon.getP(i);
			if(i == 0){
				minY = (int) points[i].y;
				maxY = (int) points[i].y;
			}else if(minY > points[i].y)
				minY = (int) points[i].y;
			else if(maxY < points[i].y)
				maxY = (int) points[i].y;
			System.out.println("Punkt "+i+": "+actPolygon.getP(i));
		}
		System.out.println("minY: "+minY+"\tmaxY: "+maxY);
		for(int c = minY+1;c < maxY;c += dist){
			pointNumber = actPolygon.getPointNumber();
			int intersec[] = new int[pointNumber];
			System.out.println("c = "+c);
			for(int t=0;t<pointNumber;t++){
				if(t<pointNumber-1){
					if(((points[t].y <= c) && (points[t+1].y > c)) || ((points[t+1].y <= c) && (points[t].y > c))){
						ix = points[t].x+((points[t+1].x-points[t].x)/(double)(points[t+1].y-points[t].y))*(c-points[t].y);
						System.out.println("Corner["+t+"] und Corner["+t+"+1]: "+(points[t].x+((points[t+1].x-points[t].x)/(double)(points[t+1].y-points[t].y))*(c-points[t].y)));
						intersec[t]  = (int)Math.round(ix);
						System.out.println("intersec["+t+"]: "+intersec[t]);
					}
				}else{
					if(((points[t].y <= c) && (points[0].y > c)) || ((points[0].y <= c) && (points[t].y > c))){
						ix = points[t].x+((points[0].x-points[t].x)/(double)(points[0].y-points[t].y))*(c-points[t].y);
						System.out.println("Corner["+t+"] und Corner[0]: "+(points[t].x+((points[0].x-points[t].x)/(double)(points[0].y-points[t].y))*(c-points[t].y)));
						intersec[t]  = (int)Math.round(ix);
						System.out.println("intersec["+t+"]: "+intersec[t]);
					}
				}
			}
			java.util.Arrays.sort(intersec);
			for (int i = 0; i < pointNumber; i++) {
				System.out.println("intersec["+i+"](sort): "+intersec[i]);
				if(pointNumber%2==0){
					if(i%2==0){
						actLine = new Line(intersec[i], c, intersec[i+1], c, fillColor);
						lineList.add(actLine);
						repaint();
					}
				}else{
					if(i%2==1){
						actLine = new Line(intersec[i], c, intersec[i+1], c, fillColor);
						lineList.add(actLine);
						repaint();
					}
				}
			}
		}	
	}
	
	/**
	 * Es werden alle Elemente aus der tmplist entfernt
	 */
	public void undoLine() {
		tmplist.removeAllElements();
		cornerCount = 0;
		repaint();
	}


	/**
	 * Notwendige Set-Methode
	 */
	private void setPolyEnde() {
		polygonList.add(actPolygon);
		actPolygon = null;
		undoLine();
	}
	/**
	 * aktuelle Fuellfarbe im PaintPanel des Polygons
	 * @param color
	 */
	public void setFillColor(Color color) {
		this.fillColor = color;
	}
	
	/**
	 * aktuelle Linienfarbe im PaintPanel
	 * @param color
	 */
	public void setLineColor(Color color) {
		this.lineColor = color;
	}
	
	/**
	 * Auswahl des Zeichnenmodus im Menuepunkt Zeichnen
	 * @param mode wird true uebergeben, wird gezeichnet
	 */
	public void setDrawMode(boolean mode) {
		this.drawMode = mode;
	}
	
	/**
	 * Auswahl des Fuellmodus im Menuepunkt Fuellen
	 * @param fillmode wird true uebergeben, wird gefuellt
	 */
	public void setFillMode(boolean fillmode) {
		this.fillMode = fillmode;
		if(fillmode)
			this.shadeDistance = 1;
	}
	
	/**
	 * Alle Elemente aus dem PaintPanel löschen
	 */
	public void clearall(){
		polygonList.removeAllElements();
		tmplist.removeAllElements();
		lineList.removeAllElements();
		repaint();
	}


	/**
	 * Zum zeichnen von Objekten auf dem PaintPanel
	 */
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D) g;
		//Zeichnen der Fuelllinien
		for(Paintable actObj : lineList)
			actObj.painting(g2d, false);
	   //Neuzeichnen aller bereits erzeugten Grafikobjekte
	   //durchgezogene Linie
		for (Paintable actObj : polygonList)
			actObj.painting(g2d, false);
	   //Zeichnen des gerade aktuellen Grafikobjekt
	   //gestrichelte Linie
		for(Paintable actObj : tmplist)
			actObj.painting(g2d, true);
		
		
		if(drawMode && actLine != null && cornerCount > 0)
			actLine.painting(g2d, true);
		
	}

	public void setShadeMode(boolean shadeMode) {
		this.shadeMode  = shadeMode;
		if(shadeMode)
			this.shadeDistance = 3;		
	}
}